
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "lrb.h"

#define lrb_log(fmt, ...)

typedef struct {
    uint8_t *buffer;
    int buffer_size;
    int head;
    int tail;
    int high_mark_val;
    int low_mark_val;
    void(*cb)(int lrb, lite_rb_evt_e evt);
} lite_rb_t;

int lite_rb_init(int buffer_size, int high_mark_val, int low_mark_val, void(*cb)(int lrb, lite_rb_evt_e evt)) {
    int ret = 0;
    lite_rb_t *lrb = calloc(1, sizeof(lite_rb_t));
    if(!lrb) {
        ret = -1;
        goto end;
    }

    lrb->buffer = malloc(buffer_size + 1);
    if(!lrb->buffer) {
        ret = -1;
        goto end;
    }

    lrb->buffer_size = buffer_size + 1;
    lrb->head = 0;
    lrb->tail = 0;
    lrb->high_mark_val = high_mark_val;
    lrb->low_mark_val = low_mark_val;
    lrb->cb = cb;

end:
    if(ret) {
        if(lrb->buffer) free(lrb->buffer);
        if(lrb) free(lrb);
        return 0;
    }

    return (int)lrb;
}

void lite_rb_deinit(int lrb) {
    if(lrb) {
        lite_rb_t *lrb_ptr = (lite_rb_t *)lrb;
        if(lrb_ptr->buffer) free(lrb_ptr->buffer);
        free(lrb_ptr);
    }
}

bool lite_rb_is_full(int lrb) {
    if(!lrb) return -1;

    lite_rb_t *lrb_ptr = (lite_rb_t *)lrb;
    return (lrb_ptr->tail + 1) % lrb_ptr->buffer_size == lrb_ptr->head;
}

bool lite_rb_is_empty(int lrb) {
    if(!lrb) return -1;

    lite_rb_t *lrb_ptr = (lite_rb_t *)lrb;
    return lrb_ptr->tail == lrb_ptr->head;
}

int lite_rb_data_size(int lrb) {
    if(!lrb) return -1;

    lite_rb_t *lrb_ptr = (lite_rb_t *)lrb;
    if(lrb_ptr->tail >= lrb_ptr->head)
        return lrb_ptr->tail - lrb_ptr->head;
    else
        return lrb_ptr->tail - lrb_ptr->head + lrb_ptr->buffer_size;
}

int lite_rb_empty_size(int lrb) {
    if(!lrb) return -1;

    lite_rb_t *lrb_ptr = (lite_rb_t *)lrb;
    return lrb_ptr->buffer_size - 1 - lite_rb_data_size(lrb);
}

int lite_rb_write(int lrb, uint8_t *data, int len) {
    if(!lrb) return -1;

    lite_rb_t *lrb_ptr = (lite_rb_t *)lrb;
    int head = lrb_ptr->head;
    int i = 0;
    int f = 1;

    for(; i < len; i++) {
        if((lrb_ptr->tail + 1) % lrb_ptr->buffer_size == head) {  // buffer is full, stop write
            if(lrb_ptr->cb) lrb_ptr->cb(lrb, LITE_RB_EVT_FULL);
            break;
        }
        if(lrb_ptr->tail >= lrb_ptr->buffer_size) lrb_ptr->tail = 0; // reach linear buffer end, turn back
        lrb_ptr->buffer[lrb_ptr->tail++] = data[i];
        if(lite_rb_data_size(lrb) >= lrb_ptr->high_mark_val) {  // high mark callback
            if(lrb_ptr->cb && f) {
                lrb_ptr->cb(lrb, LITE_RB_EVT_HIGH_MARK);
                f = 0;
            }
        }
    }

    return i;
}

int lite_rb_read(int lrb, uint8_t *data, int len) {
    if(!lrb) return -1;

    lite_rb_t *lrb_ptr = (lite_rb_t *)lrb;
    int tail = lrb_ptr->tail;
    int i = 0;
    int f = 1;

    for(; i < len; i++) {
        if(tail == lrb_ptr->head) {  // buffer is empty, stop read
            if(lrb_ptr->cb) lrb_ptr->cb(lrb, LITE_RB_EVT_EMPTY);
            break;
        }
        if(lrb_ptr->head >= lrb_ptr->buffer_size) lrb_ptr->head = 0; // reach linear buffer end, turn back
        data[i] = lrb_ptr->buffer[lrb_ptr->head++];
        if(lite_rb_data_size(lrb) <= lrb_ptr->low_mark_val) {  // low mark callback
            if(lrb_ptr->cb && f) {
                lrb_ptr->cb(lrb, LITE_RB_EVT_LOW_MARK);
                f = 0;
            }
        }
    }

    return i;
}
